Noise floor and local stats log#1566
Conversation
Conflicts resolved: - AccessoryManager+ToRadio.swift: Keep both sendLocalStatsRequest and exchangeUserInfo - NodeDetail.swift: Keep both RequestLocalStatsButton and ExchangeUserInfoButton - project.pbxproj: Add ExchangeUserInfoButton.swift to project
Add noise floor
|
Left some comments in the firmware PR, but in general I would say it would be good to have an explanation with e.g. TipKit about it. It needs to be taken with a grain of salt, and it can vary quite a bit (as you can see in 2 minutes there is already almost 10dB difference). Adding filters does not necessarily help, e.g. when the noise/interference is in-band and may skew the result, as they have an insertion loss which will result in a lower noise floor shown. |
|
Great feature that will really assist with infrastructure site selection. No more inferring noise floor via traceroute to other nodes. |
|
@copilot resolve the merge conflicts in this pull request |
@copilot implement this tipkit suggestion |
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
|
|
Merged
|
garthvh
left a comment
There was a problem hiding this comment.
Thanks for the PR — noise floor and local stats are genuinely useful diagnostics for advanced users, and the overall structure of the feature (chart + table + CSV export + request button with cooldown) is solid and matches the pattern of the existing telemetry log views. A few blocking issues need to be resolved before this can merge.
Critical — .gitmodules points to a forked protobufs submodule
The PR changes .gitmodules to point at https://github.com/RCGV1/protobufs-fork.git on branch noise-floor. This cannot be merged — the submodule must always point to https://github.com/meshtastic/protobufs.git.
The good news: noiseFloor (field 15 in LocalStats) is already present in the official protobufs on main — telemetryMessage.localStats.noiseFloor is already available as Int32. Please revert the .gitmodules change and point the submodule back to the official repo.
Critical — Wrong persistence layer for TelemetryEntity
The PR modifies Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 55.xcdatamodel and TelemetryEntity+CoreDataClass.swift. Core Data has been replaced by SwiftData in this project. On main, TelemetryEntity is a SwiftData @Model at Meshtastic/Model/TelemetryEntity.swift.
The noiseFloor property must be added to the SwiftData model:
// Meshtastic/Model/TelemetryEntity.swift
var noiseFloor: Int32?A VersionedSchema migration entry is also required in MeshtasticSchema.swift. See docs/developer/swiftdata.md for the migration pattern used in this project.
Critical — NSPredicate in LocalStatsLog and NodeDetail
NSPredicate is a Core Data API. Two places use it:
// LocalStatsLog.swift
node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 4"))
// NodeDetail.swift
node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 4")).countWith SwiftData, use Swift's native filter:
node.telemetries.filter { $0.metricsType == 4 }Critical — @Environment(.managedObjectContext) in LocalStatsLog
@Environment(\.managedObjectContext) var contextThis is the Core Data environment key. The SwiftData equivalent is:
@Environment(\.modelContext) var contextWarning — Proto type mismatch: noiseFloor is Int32, not Float
The protobuf defines noiseFloor as Int32 (whole dBm value). The PR stores and displays it as Float. Please keep it as Int32 throughout — in the SwiftData model, the CSV export, and the chart/table display. The noiseFloor != 0 nil check should also become hasValue optional pattern once stored as Int32?.
Warning — Typo in user-facing alert string
"Responses can some time."
Should be:
"Responses can take some time."
Warning — "Icky" as a localizable string
"Icky" is used as a RuleMark chart label and appears in Localizable.xcstrings with the comment "Icky" is slang for very bad. Slang does not translate well and will confuse translators. Please use a proper technical label such as "Poor Signal" or "Threshold (-85 dBm)".
Warning — Checklist not completed
All checklist items are unchecked. Please complete the self-review checklist before requesting review.
What to keep as-is
sendLocalStatsRequest()inAccessoryManager+ToRadio— clean, follows existing patterns perfectlyRateLimitedButtonwith 30s cooldown — correct approach- CSV export in
WriteCsvFile.swift— well structured - Overall
LocalStatsLogview structure (chart + table + export buttons) — matchesDeviceMetricsLogand other telemetry log views nicely noiseFloorcolour coding logic — sensible thresholds
|
Updated this branch with the review fixes. Changes made:
Validation performed:
|
✅ Docs staleness check passedThis PR includes updates to |
|
Follow-up after the docs-staleness warning: added user docs for Local Stats and noise floor in |
|
@copilot is this functionality properly gated to only the firmware versions where the readings are available? |
Firmware that predates noiseFloor support sends the proto3 default of 0 for LocalStats.noiseFloor. Real LoRa noise floors are always negative (-80 to -120 dBm), so storing 0 directly would show "0 dBm" in the UI for every older node instead of "No Reading". Treat the proto3 default as nil at storage time so that noise floor display is naturally gated to firmware versions that actually populate the field (non-zero value).
Not fully — fixed in The Since LoRa noise floors are always negative (−80 to −120 dBm), telemetry.noiseFloor = telemetryMessage.localStats.noiseFloor != 0 ? telemetryMessage.localStats.noiseFloor : nil
The |
# Conflicts: # Meshtastic.xcodeproj/project.pbxproj # Meshtastic/Helpers/MeshPackets.swift # Meshtastic/Resources/docs/index.json # Meshtastic/Views/Nodes/Helpers/NodeDetail.swift
The noise-floor branch introduced MeshtasticSchemaV2 and a V1→V2 lightweight migration for the local-stats fields. Since V1 has not shipped, there are no V1 stores to migrate from — the migration is unnecessary risk. The local-stats fields already live in the @model classes that V1 references, so fold the change into V1 and remove V2. - MeshtasticSchema: current/allModels back to V1 (also restores DeviceLinkEntity, which V2's model list omitted) - MeshtasticMigrationPlan: V1-only, no stages - Delete MeshtasticSchemaV2.swift and its project.pbxproj entries Builds clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The waypoint-editing refactor in WaypointForm.swift is unrelated to the noise floor / local stats feature. Restore it to match main so this PR is scoped to the feature; the refactor can land in its own PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
noise_floor is a plain proto3 scalar with no presence tracking, so unset and a literal 0 are indistinguishable on the wire. Document why 0 is treated as nil and what would be required (upstream optional field) for true presence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What changed?
Int32?) to the SwiftDataTelemetryEntitymodel with the requiredVersionedSchemamigration entry.RateLimitedButton) in Node Details for requesting local stats from a remote node.TipKittip inLocalStatsLogexplaining that noise floor readings are directional diagnostics that can vary quickly, and that external filters may lower the displayed value due to insertion loss or in-band interference.0; the app stores that asniland shows "No Reading" instead of0 dBm, since real LoRa noise floors are always negative."Icky"with"Threshold (-85 dBm)"."Responses can take some time.".docs/user/nodes.mdanddocs/user/telemetry.mdwith bundled in-app HTML regenerated.LocalStatsTelemetryExportTests) covering noise floor export.Why did it change?
Local stats and noise floor are high-value RF diagnostics for advanced users monitoring mesh health and receiver conditions. The
noiseFloorfield (field 15 inLocalStats) is already present in the official protobufs onmainvia firmware PR #9347.How is this tested?
Tested with custom firmware.
Localizable.xcstringsvalidated withxcrun xcstringstool compile --dry-run. Simulator app build passed withCODE_SIGNING_ALLOWED=NO.LocalStatsTelemetryExportTestsbuild-for-testing passed.Screenshots/Videos (when applicable)
Checklist
docs/user/ordocs/developer/, and updated accordingly (see copilot-instructions.md for the view → doc page mapping). If no doc update is needed, add theskip-docs-checklabel.